package com.example.easyexcel.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.example.easyexcel.config.NonClazzOrientedListener;
import com.example.easyexcel.config.UploadDataListener;
import com.example.easyexcel.domain.Archive;
import com.example.easyexcel.domain.DynamicFields;
import com.example.easyexcel.domain.StuInfo;
import com.example.easyexcel.enums.TableCodeEnum;
import com.example.easyexcel.service.ArchiveService;
import com.example.easyexcel.service.DynamicFieldsService;
import com.example.easyexcel.service.StuInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Slf4j
@RestController
@RequestMapping("/easyexcel")
@Api(tags = "测试easyexcel")
@Validated
public class EasyExcelController {

    @Autowired
    StuInfoService stuInfoService;
    @Autowired
    DynamicFieldsService dynamicFieldsService;
    @Autowired
    ArchiveService archiveService;

    /**
     * 通过注解的形式导入
     */
    @ApiOperation("只需要一个readListener，解决全部的问题")
    @PostMapping("/importData/annotation")
    @ResponseBody
    public String importDataAnnotation(MultipartFile file) throws IOException {
        try {
            EasyExcel.read(file.getInputStream(),
                            StuInfo.class,
                            new UploadDataListener<StuInfo>(
                                    list -> {
                                        // 校验数据
                                        //ValidationUtils.validate(list);
                                        // dao 保存···
                                        //最好是手写一个，不要使用mybatis-plus的一条条新增的逻辑
                                        stuInfoService.saveBatch(list);
                                        log.info("从Excel导入数据一共 {} 行 ", list.size());
                                    }))
                    .sheet()
                    .doRead();
        } catch (IOException e) {

            log.error("导入失败", e);
            throw new RuntimeException("导入失败");
        }
        return "SUCCESS";
    }


    /**
     * 实现了动态字段的导入功能
     */
    @ApiOperation("只需要一个readListener，解决全部的问题")
    @PostMapping("/importData/dynamic")
    @ResponseBody
    public String importDataDynamic(@ApiParam(value = "上传的文件", required = true) @NotNull(message = "上传文件不能为空") MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(),
                            new NonClazzOrientedListener<>(
                                    list -> {
                                        // 校验数据
                                        //ValidationUtils.validate(list);
                                        stuInfoService.saveBatch(list);
                                        log.info("从Excel导入数据一共 {} 行 ", list.size());
                                    }, TableCodeEnum.STU_INFO.getTableName(), StuInfo.class))
                    .sheet()
                    .doRead();
        } catch (IOException e) {
            log.error("导入失败", e);
            throw new RuntimeException("导入失败");
        }
        return "SUCCESS";
    }


    /**
     * 实现了动态字段的导出功能
     */
    @ApiOperation("easyexcel导出")
    @GetMapping("/exportData/dynamic")
    public String exportDataDynamic(HttpServletResponse response) {
        try {
            // 从配置获取需要导出的字段（例如：数据库、配置文件或请求参数）
            List<DynamicFields> dynamicFieldsList = dynamicFieldsService.getDataWithShow(TableCodeEnum.STU_INFO.getTableName());

            List<String> attrNames = dynamicFieldsList.stream().map(DynamicFields::getAttrName).collect(Collectors.toList());

            //属性描述（表头）
            List<List<String>> head = dynamicFieldsList.stream().map(one -> Collections.singletonList(one.getAttrDesc())).collect(Collectors.toList());

            List<StuInfo> stuInfos = stuInfoService.list();
            List<List<Object>> list = new ArrayList<>();
            for (StuInfo stuInfo : stuInfos) {
                List<Object> row = new ArrayList<>();
                for (String attrName : attrNames) {
                    Field field = ReflectionUtils.findField(StuInfo.class, attrName);
                    field.setAccessible(true);
                    Object obj = ReflectionUtils.getField(field, stuInfo);
                    row.add(obj);
                }
                list.add(row);
            }

            // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("测试", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            EasyExcel.write(response.getOutputStream()).head(head).sheet("测试").doWrite(list);
        } catch (IOException e) {
            log.error("导入失败", e);
            throw new RuntimeException("导入失败");
        }
        return "SUCCESS";
    }


    /**
     * 导出100W数据
     */
    @ApiOperation("导出100W数据")
    @PostMapping("/export100wDate")
    public String export100wDate() {
        long start = System.currentTimeMillis();

        String pathName = System.getProperty("user.dir") + "/excel/" + UUID.randomUUID() + ".xlsx";
        File parentFile = new File(pathName).getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }
        // 分页查询，每次查询1W数据，
        int _1W = 10000;
        int pageSize = _1W;
        String lastMaxDh = "";//最大档案号，随着分页的推进需要查询效率会降低，根据上一次查询的最大档案号，过滤进行下一次查询（就是查过的就过滤掉）
        ExcelWriter excelWriter = EasyExcel.write(pathName, Archive.class).build();
        // 创建writeSheet
        int sheet = 1;
        String sheetName = "sheet-" + sheet;
        WriteSheet writeSheet = EasyExcel.writerSheet(sheet, sheetName).build();
        List<Archive> archiveList;
        int writeCount = 0;
        try {
            while (true) {

                long queryStart = System.currentTimeMillis();
//                archiveList = archiveService.lambdaQuery().gt(Archive::getDh, lastMaxDh).page(new Page<>(1, pageSize)).getRecords();
                archiveList = archiveService.selectByGtDhWithLimit(lastMaxDh, pageSize);
                long queryEnd = System.currentTimeMillis();
                log.info("查询数据大小:{},耗时:{}", archiveList.size(), queryEnd - queryStart);
                //如果为空，则跳出循环
                if (CollectionUtils.isEmpty(archiveList)) {
                    break;
                }

                long writeStart = System.currentTimeMillis();
                excelWriter.write(archiveList, writeSheet);
                long writeEnd = System.currentTimeMillis();
                log.info("写入sheet位置:{}，本次写入耗时:{}", sheetName, writeEnd - writeStart);

                writeCount++;
                lastMaxDh = archiveList.get(archiveList.size() - 1).getDh();
                if (writeCount % (20 * _1W / pageSize) == 0) {
                    sheetName = "sheet-" + (++sheet);
                    writeSheet = EasyExcel.writerSheet(sheet, sheetName).build();
                    log.info("写满一个sheet,切换到下一个sheet:{}", sheetName);
                }
            }
        } catch (Exception e) {
            log.error("导出失败", e);
            return "FAIL";
        } finally {
            excelWriter.finish();
        }
        long total = System.currentTimeMillis() - start;
        log.info("导出结束，总耗时:{}", total);
        return "SUCCESS";

    }
}
